Workshop

Programmieren in Assembler - Kurs Teil 2

Richtigstellung

Im ersten Teil dieses Kurses (Ausgabe 2/97) wurde auf den Assembler AsmOne v1.29 von TFA hingewiesen. Dieser Assembler ist nun in der Version 1.30 im Aminet unter /dev/asm/ASM-One_v1.30.lha zu finden. Allerdings braucht ihr noch das Archiv /dev/asm/Asm-One_REGSDA.lha, dieses enthält die Datei REGSDATA, die im ASM-One-Archiv vergessen wurde. Ein ebenfalls guter Freeware- Assembler ist PhxAs (/dev/asm/PhxAss4.36.lha) und der dazugehörige Linker PhxLnk (/dev/asm/PhxLnk431.lha).

Software

Für diesen Teil brauchen wir noch ArtPRO, ein Grafikkonvertierprogramm. Es ist im Aminet unter /gfx/conv/ArtPRO1.03.lha zu finden. Installiert dieses Programm also erstmal.

Thema

Thema dieses Teils ist das Anzeigen und Bewegen von Grafiken. Aber bevor wir damit loslegen, erstmal Theorie.

Alles im Speicher

Wenn wir ein schönes buntes Bild auf dem Bildschirm sehen, muß es ja auch irgendwie im Amiga sein. Es befindet sich natürlich im Speicher. Um zu wissen wie es im Speicher abgelegt ist, müßt ihr erstmal wissen wie ein Bild beim Amiga aufgebaut ist.

Aufbau eines Bildes

Ein Bild besteht aus Bitplanes. Ihr könnt euch eine Bitplane als weißes Blatt Papier vorstellen. Wenn wir nun keinen Punkt auf unser Blatt malen würden, wäre unser Bildschirm schwarz (kommt drauf an welche Hintergrund- farbe gesetzt ist, standardmäßig aber schwarz). Auf eine Bitplane bezogen bedeutet das: sind alle Bits in der Bitplane 0, sehen wir auf dem Bild- schirm die gesetzte Hintergrundfarbe. Setzen wir aber ein Bit auf 1, sehen wir einen Punkt mit der Farbe 1 auf dem Bildschirm. Wir können mit einer Bitplane also nur zwei Farben verwenden: Farbe 0 (Hintergrund) und Farbe 1. Im Prinzip könnten wir so schon "malen", indem wir nur Bits in einer Bitplane setzen. Und genau das werden wir nacher auch mal tun. Nun wollen wir aber nicht nur einfarbige Bilder anzeigen (ist ja auf Dauer ziemlich eintönig), sondern auch welche mit mehr Farben. Und wie können wir nun mehrere Farben verwenden? Ganz einfach: wir benutzen mehrere Bitplanes. Die einzelnen Bitplanes "liegen" dann übereinander. Wie das dann mit dem Farben geht erkläre ich hier mal ein einem Punkt mit zwei Bitplanes:

Bitplane 1Bitplane 2Farbe
000
101
012
113

Ist ein Punkt in Bitplane 1 und Bitplanes 2 nicht gesetzt, sehen wir die Farbe 0 an dieser Stelle. Ist der Punkt in Bitplane 1 gesetzt, jedoch nicht in Bitplane 2, sehen wir Farbe 1 an dieser Stelle. Umgekehrt sehen wir Farbe 2. Ist der Punkt in beiden Bitplanes gesetzt, kommt Farbe 3 zum Ein- satz. Hier noch eine Übersicht, wie viele Farben mit wie vielen Bitplanes möglich sind:

BitplanesFarben
01
12
24
38
416
532
664
7128
8256

Auf Amigas mit OCS- und ECS-Chipset können wir 5 Bitplanes verwenden, also 32 Farben. Mit AGA-Amigas also dann 8 Bitplanes - 256 Farben. Das es aber dennoch möglich ist, mehr Farben darzustellen, werde ich nachher zeigen. Und genau so wie ein Bild aufgebaut ist, befindet es sich auch im Speicher. Erst "liegt" die Bitplane 1 im Speicher, dann die 2., dann die 3. usw. ... Das Format, wie die Bilder im Speicher liegen, hat natürlich auch einen Namen: RAW-Format. Und genau das Format müssen unsere Bilder nachher haben bevor wie sie in den Speicher laden und anzeigen.

Anzeigen eines Bildes

Und wie wird das Bild nun auf dem Bildschirm angezeigt? Der uns bekannte Copper macht mal wieder die ganze Arbeit. Wir setzen nur einige Register auf die gewünschten Werte, z.B. Auflösung, Anzahl der Bitplanes und die RGB-Werte der Farben, "sagen" dem Copper noch wo sich unsere Bitplanes im Speicher befinden und schon und wird unser Bild angezeigt.

Einstellen der Auflösung und Anzahl der Bitplanes (kein AGA)

Die Auflösung und Anzahl der Bitplanes des Bildschirmes stellen wir im Register BPLCON0 ($dff100) ein. Sehen wir uns mal die einzelnen Bits genauer an:

BitNameFunktion
15HIRESIst dieses Bit auf 1 gesetzt, so wird der hochauflösende Modus (640 Punkte pro Zeile) eingeschaltet.
14BPU2Die Bits BPU2, BPU1 und BPU0 ergeben zusammen eine 3-Bit Zahl, die angibt wieviele Planes eingeschaltet sind.

BPU2BPU1BPU0BitplanesFarben
0000nur Hintergrund
00112
01024
01138
100416
101532
110664 (Extra-Half-Bright)

13BPU1siehe BPU2
12BPU0siehe BPU2
11HMODWill man den Hold-and-Modify Modus aktivieren, so ist dieses Bit auf 1 zu setzen. In diesem Modus ist es möglich alle 4096 Farben des Amiga gleichzeitig darzustellen.
10DBPLFIst dieses Bit auf 1, so ist der Dual-Playfield-Modus aktiviert, und es werden die geraden und ungeraden Planes wie zwei unterschiedliche Bildschirme behandelt. Die Bits 10 und 11 dürfen nicht gemeinsam aktiv sein. Will man jedoch den Extra-Half-Bright-Modus (64 Farben) aktivieren, so müssen die Bits 10 und 11 auf 0 gesetzt und alle 6 Planes eingeschaltet sein.
9COLORDieses Bit schaltet den Videofarbausgang von Agnus ein.
8GAUDMit diesem Bit wird das Genlock Audio eingeschaltet.
7unbenutzt
6unbenutzt
5unbenutzt
4unbenutzt
3LPENWill man einen Lightpen abfragen, so ist dieses Bit auf 1 zu setzen.
2LACEUm den Interlace Modus zu benutzen, muß dieses Bit auf 1 gesetzt werden.
1ERSYHat dieses Bit den Wert 1, werden die Anschlüsse für horizontale und vertikale Synchronisation von Ausgang auf Eingang umgeschaltet. Dadurch kann das Amigabild durch externe Signale synchronisiert und zu jedem beliebigen Fernsehbild gemischt werden. Dieses Bit wird vor allem von Genlock-Interfaces benutzt.
0unbenutzt

Einstellen des sichtbaren Bereichs (Fenster)

Die Startposition des Fensters wird im Register DIWSTRT ($dff08e) eingestellt.

Bits:1514131211109876543210
V7V6V5V4V3V2V1V0H7H6H5H4H3H2H1H0

Die Bits H0-H7 bestimmen die horizontale Startposition und die Bits V0-V7 die vertikale. Für unsere Copperliste nehmen wir eine Startposition von vertikal 44 und horizontal 129, in hexadezimal umgerechnet ergibts das einen Wert von $2c81.

Die Endposition des Fensters tragen wir im Register DIWSTOP ($dff090) ein.

Bits:1514131211109876543210
V7V6V5V4V3V2V1V0H7H6H5H4H3H2H1H0

In diesem Register wird Bit H8 als 1 angenommen, wobei die Endposition im Bereich 256 bis 458 liegen kann. Um nun eine vertikale Endposition größer als auch kleiner als 256 zu erhalten, hat man zu einem kleinen Trick gegriffen:

Um das V8-Bit auf 0 zu setzen, schreibt man eine 1 in V7, will man V8 auf 1 setzen löscht man V7. Damit sind Positionen von 128 bis 312 möglich.

Standardwerte für DIWSTRT und DIWSTOP:DIWSTRTDIWSTOP
NTSC$2c81$f4c1
PAL$2c81$2cc1

DDFSTRT ($dff092) und DDFSTOP ($dff094)

Die Display-Data-Fetch-Start- und Display-Window-Start-Register sind voneinander anhängig. Da jede Bitplane im Lores-Modus alle 8 Zyklen eingelesen wird (im Hires-Modus 4 Zyklen), die Hardware aber noch einen halben Buszyklus zum Darstellen der Daten benötigt, besteht eine Differenz von 8.5 Zyklen. DDFSTRT errechnet sich dann wie folgt:

DDFSTRT: $81 / 2 - 8.5 = $38 (statt $81 ist der Wert aus DIWSTRT zu nehmen)

Um den Display-Data-Fetch-Stop-Wert zu berechnen, nimmt man die horizontale Auflösung, teilt diese durch 2, subtrahiert 8 und addiert den Wert aus DDFSTRT.

DDFSTOP: $38 + (320 / 2 - 8) = $d0 ($38 entspricht den DDFSTRT-Wert und 320 die horizontale Auflösung in Punkten)

Standardwerte für DDFSTRT und DDFSTOP:AuflösungDDFSTRTDDFSTOP
LoRes$0038$00d0
HiRes$003c$00d4

Woher die Daten nehmen ?

Hat man dem Amiga nun mitgeteilt, wie groß unser Fenster sein soll, muß er noch wissen woher er die Daten aus dem Speicher holen soll. Dazu bedienen wir uns der Register BPL1PTH - BPL6PTL ($dff0e0 - $dff0f8). Je nachdem wieviele Bitplanes verwenden werden sollen, müssen die BPLxPTH / BPLxPTL Pointer verwendet werden.

Modulo-Werte

Um zu verstehen was die Modulo-Werte sind, solltet ihr wissen, daß unsere Bitplanes nicht rechteckig im Speicher vorliegen. Die einzelnen Bytes einer Bitplane befinden sich alle nacheinander im Speicher. Um nun das Bild an- zuzeigen, muß der Copper die Bytes lesen und nach einer bestimmten Anzahl gelesender Bytes eine neue Zeile am Bildschirm anfangen. Bei einer horizontalen Auflösung von 320 Punkten passen genau 40 Bytes in eine Zeile. Nach 40 Bytes muß der Copper also eine neue Zeile anfangen. Der Modulo-Wert beträgt also 40. Den Modulo-Wert für die ungeraden Bitplanes (1, 3 und 5) tragen wir im Register BPL1MOD ($dff108) ein, und für die geraden Bitplanes (2, 4 und 6) im Register BPL2MOD ($dff10a). Allerdings brauchen wir bei einer horizontalen Auflösung von 320 keine 40 in die Register eintragen, daß weiß der Copper nämlich automatisch. Der Wert in den Modulo-Registern wird immer zu 40 hinzuaddiert, und das ist dann der Modulo-Wert. Bei einer horizontalen Auflösung von 640 müssten wir also 40 in die Modulo-Register eintragen, da der richtige Modulo-Wert bei dieser Auflösung 80 beträgt.

Unsere erste Bitplane

Eine Bitplane ist nur ein Stück Speicher mit dem Bildinformationen. Da wir nur eine leere Bitplane haben möchten, müssen wir uns nur ein entsprechend größes Stück Speicher löschen. Die Größe des Speichers wird in Byte angegeben und errechnet sich wie folgt:

Größe = horizontale Punkte / 8 * vertikale Punkte

Für eine Bitplane in der Größe 320 x 256 Punkte brauchen wir eine Speicher-Größe von: 320 / 8 * 256 = 10240 Bytes.

Speicher für eine Bitplane erzeugen wir im Programm mit der Anweisung

plane: ds.b 10240  ;über die symbolische Adresse "plane" können wir
                   ;auf dem Speicher zugreifen

Nun müssen wir nur noch die Adresse der Bitplane in die Bitplanepointer BPL1PTH und BPL1PTL eintragen. Da aber unser Programm und unsere Bitplane sich immer an einer anderen Stelle im Speicher befinden kann, können wir keine fixe Adresse in die Bitplanepointer eintragen. Also setzen wir die Zeiger in der Copperliste auf Null und tragen die richtige Adresse beim Start des Programms ein.

Wie das nun alles in der Praxis funktioniert, könnt ihr hier in Erfahrung bringen.

Eine Bitplane mit Muster

Nun werden wir mal unsere Bitplane mit einem Schachbrett-Muster versehen. Das Einzige was wir tun müssen besteht darin, nur einige Bits in der Bitplane auf 1 zu setzen (an dieser Stelle ist dann die Farbe in COLOR01 sichtbar. Schaut mal hier hinein, um zu sehen wir das funktioniert.

Unser erstes Bild

Nun wird es mal Zeit ein Bild anzuzeigen. Nehmt also ein Malprogramm das IFF-ILBM-Bilder speichern kann (z.B. Deluxe Paint) und malt ein Bild in der Auflösung von 320 x 256 Punkten mit 32 Farben. Wer kein Bild malen möchte, kann das Bild bild.iff nutzen das dem Kurs beiliegt. Wenn ihr damit fertig seit, startet das Programm ArtPRO.

Konvertieren ins RAW-Format

In der linken Hälfte im mittleren Drittel des Fenster von ArtPRO seht ihr das Feld "File Operation". Rechts neben dem "Load"-Button sollte UNIVERSAL stehen. Ist das nicht der Fall, klickt die 1 rechts daneben an und wählt UNIVERSAL aus. Mit einem Klick auf "Ok" beendet ihr die Eingabe. Nun habt ihr den Lader für Bilder eingestellt. UNIVERSAL lädt alle Format die ArtPRO kennt. Jetzt wollen wir noch einstellen welches Format wir speichern wollen. Klickt die 2 unter der 1 an. Wählt RAW aus und klickt anschließend auf den "Config"-Button. Bei "Save Format" sollte bei "Output" "Binary" stehen, bei "BlitWord" "None". Die Checkbox-Gadgets darunter müssen alle ausgeschalten (ohne Häckchen) sein. Klickt nun auf "Ok". Nun klickt auf "Save" um unsere Einstellungen zu speichern. Jetzt könnt ihr auf "Load" klicken um euer Bild zu laden. Anschließen klickt ihr noch auf "Save" um euer Bild im RAW-Format zu speichern. Jetzt habe wir zwar die Bilddaten im RAW-Format vorliegen, aber keine Farbpalette. Diese können wir ebenfalls mit ArtPRO speichern, und sogar schon als Ausschnitt einer Copperliste. Klickt wieder auf die 2 unter der 1 und wählt anschließend PALETTE an. Über "Config" gelangt ihr wieder ins Konfigurations-Menu. Bei "Save Format" sollte bei "Output" "Source" stehen, bei "Type" "Copper" und bei "Depth" "4 Bit". Bei "Source Format" sollt bei "Language" "Asm" stehen, bei "Width" "Words", bei "Tabs" "2" und bei "Line entries" "2". Klickt "Ok" an und anschließend "Save" um die Einstellungen zu speichern. Nun noch einmal auf "Save" klicken, und unsere Palette wird gespeichert.

Anzeigen des Bildes

Im Prinzip funktioniert das Ganze wie bisher, nur werden wir uns keine Bitplanes anlegen. Wir setzen die Bitplanepointer auf unser Bild, und schon wird es angezeigt. Klick hier drauf um das fertige Programm zu sehen.

Vertikales Scrolling

Nun wollen wir mal Bewegung in die Sache bringen. Der Fachausdruck dafür heißt "Scrolling". Zuerst schauen wir uns die einfachere Art des Scrolling an, die vertikale Richtung. Beim Scrolling bewegen wir nicht die Bitplanes selber, sonder ändern nur die Bitplanepointer. Normalerweise zeigen die Bitplanepointer auf die erste Zeile einer Bitplane. Setzen wir nun die Pointer eine Zeile tiefer, so bewegt sich der Bildschirminhalt nach oben. Umgekehrt bewegt sich der Inhalt nach unten. Bei einer horizontalen Auflösung von 320 Punkten müssen die Pointer also immer um 40 Bytes versetzt werden. Hier könnt ihr ein Beispielprogramm dazu sehen.

Horizontales Scrolling

Wenn wir horizontales Scrolling realisieren möchten, bedarf es einiger Überlegungen. Verschieben wir die Startadresse der Bitplane, so ist das leider nur um ein Word, das sind 16 Punkte, möglich. Das bedeutet unser Scrolling ruckelt fürchterlich. Mit Hilfe des Registers BPLCON1 ($dff102) können wir die Bitplane um 15 Punkte verschieben.

Bits:1514131211109876543210
----------------P2P2P2P2P1P1P1P1

Die Bits 8 - 15 sind frei. Die verbleibenden 8 Bits teilen sich die geraden und ungeraden Bitplanes, wobei P1 die geraden und P2 die ungeraden Planes darstellen. Das Einzige was wir nun tun müssen, ist die Bitplane mit Hilfe von BPLCON1 solange zu verschieben bis die 15 Punkte erreicht sind, danach ein Word auf die Stardadresse der Plane zu addieren und BPLCON1 auf Null setzen. Außerdem muß der Data-Fetch ein Word früher starten, also ist für DDFSTRT der Wert $30 statt des Standardwertes $38 einzutragen. Die Modulo-Werte in BPL1MOD und BPL2MOD müssen ebenfalls um 2 Byte weniger geändert werden. Wie das in der Praxis funktioniert, seht ihr hier.

So, das war es mal wieder. Schluß für heute. Und wie immer am Schluß: Viel Glück beim Programmieren.

(c) 1997 by Enrico Bauermeister

Zurück Weiter